home *** CD-ROM | disk | FTP | other *** search
/ LiquidLibrary 2005 September / LiquidLibrary 2005 Sep - Disc 1.iso / pc / Portfolio Browser / Filters / PDF / LIB / pdf_base.ps < prev    next >
Text File  |  2003-01-03  |  21KB  |  630 lines

  1. %    Copyright (C) 1994, 1996, 1997, 1998, 1999, 2000 Aladdin Enterprises.  All rights reserved.
  2. % This software is licensed to a single customer by Artifex Software Inc.
  3. % under the terms of a specific OEM agreement.
  4.  
  5. % $RCSfile$ $Revision$
  6. % pdf_base.ps
  7. % Basic parser for PDF reader.
  8.  
  9. % This handles basic parsing of the file (including the trailer
  10. % and cross-reference table), as well as objects, object references,
  11. % streams, and name/number trees; it doesn't include any facilities for
  12. % making marks on the page.
  13.  
  14. /.setlanguagelevel where { pop 2 .setlanguagelevel } if
  15. .currentglobal true .setglobal
  16. /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
  17. pdfdict begin
  18.  
  19. % Define the name interpretation dictionary for reading values.
  20. /valueopdict mark
  21.   (<<) cvn { mark } bind    % don't push an actual mark!
  22.   (>>) cvn /.dicttomark load
  23.   ([) cvn { mark } bind        % ditto
  24.   (]) cvn dup load
  25. %  /true true        % see .pdfexectoken below
  26. %  /false false        % ibid.
  27. %  /null null        % ibid.
  28.   /F dup cvx        % see Objects section below
  29.   /R dup cvx        % see Objects section below
  30.   /stream dup cvx    % see Streams section below
  31. .dicttomark readonly def
  32.  
  33. % ------ Utilities ------ %
  34.  
  35. % Define a scratch string.  The PDF language definition says that
  36. % no line in a PDF file can exceed 255 characters.
  37. /pdfstring 255 string def
  38.  
  39. % Read the previous line of a file.  If we aren't at a line boundary,
  40. % read the line containing the current position.
  41. % Skip any blank lines.
  42. /prevline        % - prevline <startpos> <substring>
  43.  { PDFfile fileposition dup () pdfstring
  44.    2 index 257 sub 0 .max PDFfile exch setfileposition
  45.     {        % Stack: initpos linepos line string
  46.       PDFfile fileposition
  47.       PDFfile 2 index readline pop
  48.       dup length 0 gt
  49.        { 3 2 roll 5 -2 roll pop pop 2 index }
  50.        { pop }
  51.       ifelse
  52.         % Stack: initpos linepos line string startpos
  53.       PDFfile fileposition 5 index ge { exit } if
  54.       pop
  55.     }
  56.    loop pop pop 3 -1 roll pop
  57.  } bind def
  58.  
  59. % Handle the PDF 1.2 #nn escape convention when reading from a file.
  60. % This should eventually be done in C.
  61. /.pdffixname {            % <execname> .pdffixname <execname'>
  62.   PDFversion 1.2 ge {
  63.     dup .namestring (#) search {
  64.       name#escape cvn exch pop
  65.     } {
  66.       pop
  67.     } ifelse
  68.   } if
  69. } bind def
  70. /name#escape            % <post> <(#)> <pre> name#escape <string>
  71. { exch pop
  72.   1 index 2 () /SubFileDecode filter dup (x) readhexstring
  73.         % Stack: post pre stream char t/f
  74.   not { /.pdftoken cvx /syntaxerror signalerror } if
  75.   exch closefile concatstrings
  76.   exch 2 1 index length 2 sub getinterval
  77.   (#) search { name#escape } if concatstrings
  78. } bind def
  79.  
  80. % Execute a file, interpreting its executable names in a given
  81. % dictionary.  The name procedures may do whatever they want
  82. % to the operand stack.
  83. /.pdftokenerror {        % <count> <opdict> <errtoken> .pdftokenerror -
  84.   BXlevel 0 le {
  85.     (%stderr) (w) file
  86.     dup (****************Unknown operator: ) writestring
  87.     dup 2 index .writecvs dup (\n) writestring flushfile
  88.   } if pop pop
  89.   count exch sub { pop } repeat    % pop all the operands
  90. } bind def
  91. /.pdfexectoken {        % <count> <opdict> <exectoken> .pdfexectoken ?
  92.   DEBUG { dup == flush } if
  93.   2 copy .knownget {
  94.     exch pop exch pop exch pop exec
  95.   } {
  96.         % Normally, true, false, and null would appear in opdict
  97.         % and be treated as "operators".  However, there is a
  98.         % special fast case in the PostScript interpreter for names
  99.         % that are defined in, and only in, systemdict and/or
  100.         % userdict: putting these three names in the PDF dictionaries
  101.         % destroys this property for them, slowing down their
  102.         % interpretation in all PostScript code.  Therefore, we
  103.         % check for them explicitly here instead.
  104.     dup dup dup /true eq exch /false eq or exch /null eq or {
  105.       exch pop exch pop //systemdict exch get
  106.     } {
  107.       .pdftokenerror
  108.     } ifelse
  109.   } ifelse
  110. } bind def
  111. /.pdfrun {            % <file> <opdict> .pdfrun -
  112.     % Construct a procedure with the stack depth, file and opdict
  113.     % bound into it.
  114.   1 index cvlit count 2 sub 3 1 roll mark mark 5 2 roll
  115.   {    % Stack: ..operands.. count opdict file
  116.     token {
  117.       dup type /nametype eq {
  118.     dup xcheck {
  119.       .pdfexectoken
  120.     } {
  121.       .pdffixname
  122.       exch pop exch pop DEBUG { dup ==only ( ) print flush } if
  123.     } ifelse
  124.       } {
  125.     exch pop exch pop DEBUG { dup ==only ( ) print flush } if
  126.       } ifelse
  127.     } {
  128.       (%%EOF) cvn cvx .pdfexectoken
  129.     } ifelse
  130.   }
  131.   aload pop .packtomark cvx
  132.   /loop cvx 2 packedarray cvx
  133.   { stopped /PDFsource } aload pop
  134.   PDFsource
  135.   { store { stop } if } aload pop .packtomark cvx
  136.   /PDFsource 3 -1 roll store exec
  137. } bind def
  138.  
  139. % Execute a file, like .pdfrun, for a marking context.
  140. % This temporarily rebinds LocalResources and DefaultMatrix.
  141. /.pdfruncontext {        % <resdict> <file> <opdict> .pdfruncontext -
  142.   /.pdfrun load LocalResources DefaultMatrix
  143.   /LocalResources 7 -1 roll store
  144.   /DefaultMatrix matrix currentmatrix store
  145.   3 .execn
  146.   /DefaultMatrix exch store
  147.   /LocalResources exch store
  148. } bind def
  149.  
  150. % Get the depth of the PDF operand stack.  The main program (pdf_main.ps)
  151. % sets pdfemptycount before calling .pdfrun.
  152. /.pdfcount {        % - .pdfcount <count>
  153.   count pdfemptycount sub
  154. } bind def
  155.  
  156. % ------ File reading ------ %
  157.  
  158. % Read the cross-reference entry for an (unresolved) object.
  159. % The caller must save and restore the PDFfile position if desired.
  160. % For invalid (free) objects, we return 0.
  161. /readxrefentry        % <object#> readxrefentry <objpos>
  162.  { dup Objects exch lget
  163.    PDFfile exch setfileposition
  164.    PDFfile token pop        % object position
  165.    PDFfile token pop        % generation #
  166.    PDFfile token pop        % n or f
  167.    dup /n eq
  168.     { pop 1 add dup 255 gt
  169.        { Generations ltype /stringtype eq
  170.       {        % Convert Generations from a string to an array.
  171.         larray Generations llength lgrowto dup
  172.         0 1 2 index llength 1 sub
  173.          { Generations 1 index lget lput dup
  174.          }
  175.         for pop /Generations exch store
  176.       }
  177.      if
  178.        }
  179.       if
  180.     }
  181.     { /f eq
  182.        { pop 0 }
  183.        { /readxrefentry cvx /syntaxerror signalerror }
  184.       ifelse
  185.     }
  186.    ifelse
  187.         % Stack: obj# objpos 1+gen#
  188.    Generations 4 -1 roll 3 -1 roll lput
  189.  } bind def
  190.  
  191. % ================================ Objects ================================ %
  192.  
  193. % Since we may have more than 64K objects, we have to use a 2-D array to
  194. % hold them (and the parallel Generations structure).
  195. /lshift 9 def
  196. /lnshift lshift neg def
  197. /lsubmask 1 lshift bitshift 1 sub def
  198. /lsublen lsubmask 1 add def
  199. /larray {    % - larray <larray>
  200.   [ [] ]
  201. } bind def
  202. /lstring {    % - lstring <lstring>
  203.   [ () ]
  204. } bind def
  205. /ltype {    % <lseq> type <type>
  206.   0 get type
  207. } bind def
  208. /lget {        % <lseq> <index> lget <value>
  209.   dup //lsubmask and 3 1 roll //lnshift bitshift get exch get
  210. } bind def
  211. /lput {        % <lseq> <index> <value> lput -
  212.   3 1 roll
  213.   dup //lsubmask and 4 1 roll //lnshift bitshift get
  214.   3 1 roll put
  215. } bind def
  216. /llength {    % <lseq> llength <length>
  217.   dup length 1 sub dup //lshift bitshift
  218.   3 1 roll get length add
  219. } bind def
  220. % lgrowto assumes newlength > llength(lseq)
  221. /growto {    % <string/array> <length> growto <string'/array'>
  222.   1 index type /stringtype eq { string } { array } ifelse
  223.   2 copy copy pop exch pop
  224. } bind def
  225. /lgrowto {    % <lseq> <newlength> lgrowto <lseq'>
  226.     dup //lsubmask add //lnshift bitshift dup 3 index length gt {
  227.     % Add more sub-arrays.  Start by completing the last existing one.
  228.         % Stack: lseq newlen newtoplen
  229.     3 -1 roll dup llength 1 sub //lsubmask or 1 add lgrowto
  230.         % Stack: newlen newtoplen lseq
  231.     [ exch aload pop
  232.     counttomark 2 add -1 roll        % newtoplen
  233.     counttomark sub { dup 0 0 getinterval lsublen growto } repeat
  234.     dup 0 0 getinterval ] exch
  235.   } {
  236.     pop
  237.   } ifelse
  238.     % Expand the last sub-array.
  239.   1 sub //lsubmask and 1 add
  240.   exch dup dup length 1 sub 2 copy
  241.         % Stack: newsublen lseq lseq len-1 lseq len-1
  242.   get 5 -1 roll growto put
  243. } bind def
  244. /lforall {    % <lseq> <proc> lforall -
  245.   /forall cvx 2 packedarray cvx forall
  246. } bind def
  247.  
  248. % We keep track of PDF objects using the following PostScript variables:
  249. %
  250. %    Generations (lstring): Generations[N] holds 1+ the current
  251. %        generation number for object number N.  (As far as we can tell,
  252. %        this is needed only for error checking.)  For free objects,
  253. %        Generations[N] is 0.
  254. %
  255. %    Objects (larray): If object N is loaded, Objects[N] is the actual
  256. %        object; otherwise, Objects[N] is an executable integer giving
  257. %        the file offset of the object's entry in the cross-reference
  258. %        table.
  259. %
  260. %    GlobalObjects (dictionary): If object N has been resolved in
  261. %        global VM, GlobalObjects[N] is the same as Objects[N]
  262. %        (except that GlobalObjects itself is stored in global VM,
  263. %        so the entry will not be deleted at the end of the page).
  264. %
  265. %    IsGlobal (lstring): IsGlobal[N] = 1 iff object N was resolved in
  266. %        global VM.  This is an accelerator to avoid having to do a
  267. %        dictionary lookup in GlobalObjects when resolving every object.
  268.  
  269. % Initialize the PDF object tables.
  270. /initPDFobjects {        % - initPDFobjects -
  271.   /Objects larray def
  272.   /Generations lstring def
  273.   .currentglobal true .setglobal
  274.   /GlobalObjects 20 dict def
  275.   .setglobal
  276.   /IsGlobal lstring def
  277. } bind def
  278.  
  279. % Grow the tables to a specified size.
  280. /growPDFobjects {        % <minsize> growPDFobjects -
  281.   dup Objects llength gt {
  282.     dup Objects exch lgrowto /Objects exch def
  283.   } if
  284.   dup Generations llength gt {
  285.     dup Generations exch lgrowto /Generations exch def
  286.   } if
  287.   dup IsGlobal llength gt {
  288.     dup IsGlobal exch lgrowto /IsGlobal exch def
  289.   } if
  290.   pop
  291. } bind def
  292.  
  293. % We represent an unresolved object reference by a procedure of the form
  294. % {obj# gen# resolveR}.  This is not a possible PDF object, because PDF has
  295. % no way to represent procedures.  Since PDF in fact has no way to represent
  296. % any PostScript object that doesn't evaluate to itself, we can 'force'
  297. % a possibly indirect object painlessly with 'exec'.
  298. % Note that since we represent streams by executable dictionaries
  299. % (see below), we need both an xcheck and a type check to determine
  300. % whether an object has been resolved.
  301. /resolved? {        % <object#> resolved? <value> true
  302.             % <object#> resolved? false
  303.   Objects 1 index lget dup xcheck {
  304.     dup type /integertype eq {
  305.         % Check whether the object is in GlobalObjects.
  306.       pop IsGlobal 1 index lget 0 eq {
  307.     pop false
  308.       } {
  309.         % Update Objects from GlobalObjects
  310.     DEBUG { (%Global=>local: ) print dup == } if
  311.     GlobalObjects 1 index get dup Objects 4 1 roll lput true
  312.       } ifelse
  313.     } {
  314.       exch pop true
  315.     } ifelse
  316.   } {
  317.     exch pop true
  318.   } ifelse
  319. } bind def
  320. /oforce /exec load def
  321. /oget {        % <array> <index> oget <object>
  322.         % <dict> <key> oget <object>
  323.         % Before release 6.20, this procedure stored the resolved
  324.         % object back into the referring slot.  In order to support
  325.         % PDF linearization, we no longer do this.
  326.   get oforce
  327. } bind def
  328. % A null value in a dictionary is equivalent to an omitted key;
  329. % we must check for this specially.
  330. /knownoget {    % <dict> <key> knownoget <value> true
  331.         % <dict> <key> knownoget false
  332.         % See oget above regarding this procedure.
  333.   .knownget {
  334.     oforce dup null eq { pop false } { true } ifelse
  335.   } {
  336.     false
  337.   } ifelse
  338. } bind def
  339.  
  340. % PDF 1.1 defines a 'foreign file reference', but not its meaning.
  341. % Per the specification, we convert these to nulls.
  342. /F {        % <file#> <object#> <generation#> F <object>
  343.         % Some PDF 1.1 files use F as a synonym for f!
  344.    .pdfcount 3 lt { f } { pop pop pop null } ifelse
  345. } bind def
  346.  
  347. /checkgeneration {  % <object#> <generation#> checkgeneration <object#> <OK>
  348.   Generations 2 index lget 1 sub 1 index eq {
  349.     pop true
  350.   } {
  351.     QUIET not {
  352.       Generations 2 index lget 0 eq {
  353.     (Warning: reference to free object: )
  354.       } {
  355.     (Warning: wrong generation: )
  356.       } ifelse print 1 index =only ( ) print =only ( R) =
  357.     } {
  358.       pop
  359.     } ifelse false
  360.   } ifelse
  361. } bind def
  362. /R {        % <object#> <generation#> R <object>
  363.   /resolveR cvx 3 packedarray cvx
  364. } bind def
  365.  
  366. % If we encounter an object definition while reading sequentially,
  367. % we just store it away and keep going.
  368. /objopdict mark
  369.   valueopdict { } forall
  370.   /endobj dup cvx
  371. .dicttomark readonly def
  372. /obj {            % <object#> <generation#> obj <object>
  373.   PDFfile objopdict .pdfrun
  374. } bind def
  375. /endobj {        % <object#> <generation#> <object> endobj <object>
  376.   3 1 roll
  377.         % Read the xref entry if we haven't yet done so.
  378.         % This is only needed for generation # checking.
  379.   1 index resolved? {
  380.     pop
  381.   } {
  382.     PDFfile fileposition
  383.     2 index readxrefentry pop
  384.     PDFoffset add PDFfile exch setfileposition
  385.   } ifelse
  386.   checkgeneration {
  387.         % The only global objects we bother to save are
  388.         % (resource) dictionaries.
  389.     1 index dup gcheck exch type /dicttype eq and {
  390.       DEBUG { (%Local=>global: ) print dup == } if
  391.       GlobalObjects 1 index 3 index put
  392.       IsGlobal 1 index 1 put
  393.     } if
  394.     Objects exch 2 index lput
  395.   } {
  396.     pop pop null
  397.   } ifelse
  398. } bind def
  399.  
  400. % When resolving an object reference, we stop at the endobj.
  401. /resolveopdict mark
  402.   valueopdict { } forall
  403.   /endobj { endobj exit } bind
  404.                 % OmniForm generates PDF file with endobj missing in some
  405.                 % objects. AR ignores this. So we have to do it too.
  406.   /obj { pop pop endobj exit } bind
  407. .dicttomark readonly def
  408. /resolveR {        % <object#> <generation#> resolveR <object>
  409.   DEBUG { (%Resolving: ) print 2 copy 2 array astore == } if
  410.   1 index resolved? {
  411.     exch pop exch pop
  412.   } {
  413.     PDFfile fileposition 3 1 roll
  414.     1 index readxrefentry
  415.     3 1 roll checkgeneration {
  416.             % Stack: savepos objpos obj#
  417.      exch PDFoffset add PDFfile exch setfileposition
  418.      PDFfile token pop 2 copy ne
  419.       { (xref error!) = /resolveR cvx /rangecheck signalerror
  420.       }
  421.      if pop PDFfile token pop
  422.      PDFfile token pop /obj ne
  423.       { (xref error!) = /resolveR cvx /rangecheck signalerror
  424.       }
  425.      if
  426.      pdf_run_resolve    % PDFfile resolveopdict .pdfrun
  427.     }
  428.     {        % Don't cache if the generation # is wrong.
  429.      pop pop null
  430.     } ifelse
  431.     exch PDFfile exch setfileposition
  432.   } ifelse
  433. } bind def      
  434.  
  435. % ================================ Streams ================================ %
  436.  
  437. % We represent a stream by an executable dictionary that contains,
  438. % in addition to the contents of the original stream dictionary:
  439. %    /File - the file or string where the stream contents are stored,
  440. %      if the stream is not an external one.
  441. %    /FilePosition - iff File is a file, the position in the file
  442. %      where the contents start.
  443. %    /StreamKey - the key used to decrypt this stream, if any.
  444. % We do the real work of constructing the data stream only when the
  445. % contents are needed.
  446.  
  447. % Construct a stream.  The length is not reliable in the face of
  448. % different end-of-line conventions, but it's all we've got.
  449. %
  450. % PDF files are inconsistent about what may fall between the 'stream' keyword
  451. % and the actual stream data, and it appears that no one algorithm can
  452. % detect this reliably.  We used to try to guess whether the file included
  453. % extraneous \r and/or \n characters, but we no longer attempt to do so,
  454. % especially since the PDF 1.2 specification states flatly that the only
  455. % legal terminators following the 'stream' keyword are \n or \r\n, both of
  456. % which are properly skipped and discarded by the token operator.
  457. /stream {    % <dict> stream <modified_dict>
  458.   dup /F known dup PDFsource PDFfile eq or {
  459.     not {
  460.       dup /File PDFfile put
  461.       dup /FilePosition PDFfile fileposition put
  462.       DEBUG { (%FilePosition: ) print dup /FilePosition get == } if
  463.     } if
  464.     PDFfile fileposition 1 index /Length oget add
  465.       PDFfile exch setfileposition
  466.   } {
  467.     pop
  468.     % We're already reading from a stream, which we can't reposition.
  469.     % Capture the sub-stream contents in a string.
  470.     dup /Length oget string PDFsource exch readstring
  471.     not {
  472.       (Unexpected EOF in stream!) =
  473.       /stream cvx /rangecheck signalerror
  474.     } if
  475.     1 index exch /File exch put
  476.   } ifelse
  477.   PDFsource token pop
  478.     /endstream ne { /stream cvx /syntaxerror signalerror } if
  479.   cvx
  480. } bind def
  481. /endstream {
  482.   exit
  483. } bind def
  484.  
  485. % Contrary to the published PDF (1.3) specification, Acrobat Reader
  486. % accepts abbreviated filter names everywhere, not just for in-line images,
  487. % and some applications (notably htmldoc) rely on this.
  488. /unabbrevfilterdict mark
  489.   /AHx /ASCIIHexDecode  /A85 /ASCII85Decode  /CCF /CCITTFaxDecode
  490.   /DCT /DCTDecode  /Fl /FlateDecode  /LZW /LZWDecode  /RL /RunLengthDecode
  491. .dicttomark readonly def
  492.  
  493. % Extract and apply filters.
  494. /filterparms {        % <dict> <DPkey> <Fkey> filterparms
  495.             %   <dict> <parms> <filternames>
  496.   2 index exch .knownget {
  497.     exch 2 index exch .knownget {
  498.         % Both filters and parameters.
  499.       exch dup type /nametype eq {
  500.     1 array astore exch 1 array astore exch
  501.       } if
  502.     } {
  503.         % Filters, but no parameters.
  504.       null exch
  505.       dup type /nametype eq { 1 array astore } if
  506.     } ifelse
  507.   } {
  508.         % No filters: ignore parameters, if any.
  509.     pop null { }
  510.   } ifelse
  511. } bind def
  512. /filtername {        % <filtername> filtername <filtername'>
  513.   //unabbrevfilterdict 1 index .knownget { exch pop } if
  514. } bind def
  515. /applyfilters {        % <parms> <source> <filternames> applyfilters <stream>
  516.   2 index null eq {
  517.     { filtername filter }
  518.   } {
  519.     {        % Stack: parms stream filtername
  520.       2 index 0 oget dup null eq { pop } { exch } ifelse filtername filter
  521.       exch dup length 1 sub 1 exch getinterval exch
  522.     }
  523.   } ifelse forall exch pop
  524. } bind def
  525.  
  526. % Resolve a stream dictionary to a PostScript stream.
  527. % Streams with no filters require special handling:
  528. %    - If we are going to interpret their contents, we let endstream
  529. %      terminate the interpretation loop;
  530. %    - If we are just going to read data from them, we impose
  531. %      a SubFileDecode filter that reads just the requisite amount of data.
  532. % Note that, in general, resolving a stream repositions PDFfile.
  533. % Clients must save and restore the position of PDFfile themselves.
  534. /resolvestream {    % <streamdict> <readdata?> resolvestream <stream>
  535.   1 index /F .knownget {
  536.         % This stream is stored on an external file.
  537.     (r) file 3 -1 roll
  538.     /FDecodeParms /FFilter filterparms
  539.         % Stack: readdata? file dict parms filternames
  540.     4 -1 roll exch
  541.     pdf_decrypt_stream
  542.     applyfilters
  543.   } {
  544.     exch dup /FilePosition .knownget {
  545.       1 index /File get exch setfileposition
  546.     } if
  547.         % Stack: readdata? dict
  548.     /DecodeParms /Filter filterparms
  549.         % Stack: readdata? dict parms filternames
  550.     2 index /File get exch
  551.         % Stack: readdata? dict parms file/string filternames
  552.     pdf_decrypt_stream        % add decryption if needed
  553.     dup length 0 eq {
  554.         % All the PDF filters have EOD markers, but in this case
  555.         % there is no specified filter.
  556.       pop exch pop
  557.         % Stack: readdata? dict file/string
  558.       2 index {
  559.         % We're going to read data; use a SubFileDecode filter.
  560.     1 index /Length oget () /SubFileDecode filter
  561.       } {
  562.     dup type /filetype ne {
  563.         % Use a SubFileDecode filter to read from a string.
  564.       0 () /SubFileDecode filter
  565.     } if
  566.       } ifelse
  567.     } {
  568.       applyfilters
  569.     } ifelse
  570.   } ifelse
  571.         % Stack: readdata? dict file
  572.   exch pop exch pop
  573. } bind def
  574.  
  575. % ============================ Name/number trees ============================ %
  576.  
  577. /nameoget {        % <nametree> <key> nameoget <obj|null>
  578.   exch /Names exch .treeget
  579. } bind def
  580.  
  581. /numoget {        % <numtree> <key> numoget <obj|null>
  582.   exch /Nums exch .treeget
  583. } bind def
  584.  
  585. /.treeget {        % <key> <leafkey> <tree> .treeget <obj|null>
  586.   dup /Kids knownoget {
  587.     exch pop .branchget
  588.   } {
  589.     exch get .leafget
  590.   } ifelse
  591. } bind def
  592.  
  593. /.branchget {        %  <key> <leafkey> <kids> .branchget <obj|null>
  594.   dup length 0 eq {
  595.     pop pop pop null
  596.   } {
  597.     dup length -1 bitshift 2 copy oget
  598.             % Stack: key leafkey kids mid kids[mid]
  599.     dup /Limits oget aload pop
  600.             % Stack: key leafkey kids mid kids[mid] min max
  601.     6 index lt {
  602.       pop pop
  603.       1 add 1 index length 1 index sub getinterval .branchget
  604.     } {
  605.       5 index gt {
  606.     pop
  607.     0 exch getinterval .branchget
  608.       } {
  609.     exch pop exch pop .treeget
  610.       } ifelse
  611.     } ifelse
  612.   } ifelse
  613. } bind def
  614.  
  615. /.leafget {        % <key> <pairs> .leafget <obj|null>
  616.   dup length 2 eq {
  617.     dup 0 get 2 index eq { 1 oget } { pop null } ifelse
  618.     exch pop
  619.   } {
  620.     dup length -1 bitshift -2 and 2 copy oget
  621.             % Stack: key pairs mid pairs[mid]
  622.     3 index gt { 0 exch } { 1 index length 1 index sub } ifelse
  623.     getinterval .leafget
  624.   } ifelse
  625. } bind def
  626.  
  627. end            % pdfdict
  628. .setglobal
  629.